﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using APC.Common;
using System.Data;
using System.Diagnostics;


namespace APC
{
    class Recipe
    {
        #region Enum

        public enum eActionType 
        { 
            TypeNew, 
            TypeEdit, 
            TypeDeleteRecipe,  //Delete the Recipe
            TypeDeleteRawMaterial //Delete relation between a Recipe and a RawMaterial
        }

        #endregion

        #region Declarations

        private DataHandlingDataContext ctx;

        #endregion

        #region Properties

        public int Id
        {
            get { return this._Id; }
            set { this._Id = value; }
        }
        private int _Id;

        public int RawMaterialId
        {
            get { return this._RawMaterialId; }
            set { this._RawMaterialId = value; }
        }
        private int _RawMaterialId;

        public String RecipeName
        {
            get { return this._RecipeName; }
            set { this._RecipeName = value; }
        }
        private String _RecipeName;

        public String Note
        {
            get { return this._Note; }
            set { this._Note = value; }
        }
        private String _Note;

        public String RecipeNumber
        {
            get { return this._RecipeNumber; }
            set { this._RecipeNumber = value; }
        }
        private String _RecipeNumber;

        public String EndProductName
        {
            get { return this._EndProductName; }
            set { this._EndProductName = value; }
        }
        private String _EndProductName;

        /// <summary>
        /// Recommended Batch Size
        /// </summary>
        public Int32 RecBatchSize
        {
            get { return this._RecBatchSize; }
            set { this._RecBatchSize = value; }
        }
        private Int32 _RecBatchSize;

        /// <summary>
        /// Default Order Quantum
        /// </summary>
        public Int32 DefaultOrderQuantum
        {
            get { return this._DefaultOrderQuantum; }
            set { this._DefaultOrderQuantum = value; }
        }
        private Int32 _DefaultOrderQuantum;


        /// <summary>
        /// Minimum Size of a batch
        /// </summary>
        public int MinBatchSize
        {
            get { return this._MinBatchSize; }
            set { this._MinBatchSize = value; }
        }
        private int _MinBatchSize;

        public Boolean IsSaved
        {
            set { _IsSaved = value; }
            get { return _IsSaved; }
        }
        private Boolean _IsSaved = false;

        /// <summary>
        /// Returns full list of all raw materials
        /// </summary>
        public List<APC.RawMaterials> RawMaterialList
        {
            set { this._RawMaterialList = value; }
            get
            {
                return this._RawMaterialList;
            }
        }
        private List<APC.RawMaterials> _RawMaterialList;

        /// <summary>
        /// Returns full list of all raw materials in negative list
        /// </summary>
        public List<RawMaterials> RawMaterialNegativeList
        {
            set { this._RawMaterialNegativeList = value; }
            get
            {
                return this._RawMaterialNegativeList;
            }
        }
        private List<RawMaterials> _RawMaterialNegativeList;

        public List<APC.Common.GetRecipeIngredientsResult> IngredientList
        {
            set { this._IngredientList = value; }
            get { return this._IngredientList; }
        }
        private List<APC.Common.GetRecipeIngredientsResult> _IngredientList;

        public List<APC.Common.Recipe> RecipeList
        {
            set { this._RecipeList = value; }
            get { return this._RecipeList; }
        }
        private List<APC.Common.Recipe> _RecipeList;

        /// <summary>
        /// Enum: TypeSave, TypeUpdate, TypeDelete
        /// </summary>
        public eActionType ActionType
        {
            get { return _ActionType; }
            set { this._ActionType = value; }
        }
        private eActionType _ActionType;

        #endregion

        #region Constructor

        public Recipe(int RecipeId)
        {
            ctx = new DataHandlingDataContext(new Data().ConnectionString);

            this._Id = RecipeId;
            this.LoadRecipe();
        }

        public Recipe()
        {
            ctx = new DataHandlingDataContext(new Data().ConnectionString);
            LoadRecipeList();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Returns list of raw material on negative list for selected recipe
        /// </summary>
        private void xLoadRawMaterialNegativeList()
        {
            ctx = new DataHandlingDataContext(new Data().ConnectionString);

            using (ctx)
            {
                var query = (from n in ctx.NegativeRawMaterials
                             from r in ctx.RawMaterials
                             where n.RawMaterialId == r.Id && n.RecipeId == _Id
                             select new
                             {
                                 Flush = n.Flush,
                                 Id = r.Id,
                                 Name = r.Name,
                                 Number = r.MaterialNumber
                             })
                             .Select(x => new RawMaterials()
                             {
                                 Id = x.Id,
                                 Name = x.Name,
                                 Number = x.Number,
                                 BatchFlush = Convert.ToInt16(x.Flush)
                             }).ToList();

                if (query != null)
                {
                    this._RawMaterialNegativeList = query.ToList<RawMaterials>();
                }

            }
        }

        /// <summary>
        /// Loads properties of selected recipe + ingrident list
        /// </summary>
        private void LoadRecipe()
        {

            List<APC.RawMaterials> lstRawMaterials;

            using (ctx)
            {
                var query = ctx.Recipes.FirstOrDefault(x => x.Id == _Id);
                
                if (query != null)
                {
                    this._RecipeNumber = query.RecipeNumber != null ? query.RecipeNumber.Trim() : "";
                    this._RecipeName = query.RecipeName != null ? query.RecipeName.Trim() : "";
                    this._RecBatchSize = System.Convert.ToInt32(query.RecBatchSize);
                    this._MinBatchSize = System.Convert.ToInt32(query.MinBatchSize);
                    this._DefaultOrderQuantum = Convert.ToInt32(query.DefaultOrderQuantum);
                    this._EndProductName = query.EndProductName != null ? query.EndProductName.Trim() : "";
                    this._Note = query.Description != null ? query.Description.Trim() : "";

                    //Get related ingridient list for recipe
                    lstRawMaterials = (from r in ctx.GetRecipeIngredients(_Id)
                                  select r).Select(x => new RawMaterials()
                                  {
                                    Id = x.Id,
                                    RecipeId = x.RecipeId,
                                    Name = x.RawMaterialName,
                                    Number = x.MaterialNumber,
                                    AmountProcent = Convert.ToInt64(x.AmountProcent),
                                    Tolerance = Convert.ToDecimal(x.ToleranceProcent),
                                    TagListeGroupId = -1,
                                    TagListeId = -1,
                                    LocationPosition = -1, 
                                    ColorMap = "#FFFFFF",
                                    LocationName = "Not set" //TODO: incoparate flag logic since text can differ on language!
                                  }).ToList<APC.RawMaterials>();

                    //Get related location for ingridient (rawmaterial)
                    foreach (RawMaterials rm in lstRawMaterials) 
                    {
                        var l = (from rml in ctx.RawMaterialsLocations
                                 from loc in ctx.Locations
                                 from t in ctx.TagListGroups
                                 where rml.RawMateralId == rm.Id &&
                                 rml.LocationsId == loc.Id &&
                                 loc.TagListeGroupId == t.Id
                                 select new {
                                         TagListeGroupid = loc.TagListeGroupId,
                                         //TagListeId = loc.Id,
                                         LocationId = loc.Id,
                                         LocationPosition = loc.LocationPosition,
                                         LocationName = loc.Name,
                                         ColorMap = t.ColorMap
                                 }).ToList();

                        if (l != null)
                        {
                            //If there is only one location then add this information
                            //If 0 ore more than one locacation user must select location via "Select Location"
                            if (l.Count == 1) // There is only 1 relation 
                            {
                                rm.LocationId = l.FirstOrDefault().LocationId;
                                rm.LocationName = l.FirstOrDefault().LocationName;
                                rm.LocationPosition = Convert.ToInt16(l.FirstOrDefault().LocationPosition);
                                rm.ColorMap = l.FirstOrDefault().ColorMap;
                                rm.TagListeGroupId = Convert.ToInt16(l.FirstOrDefault().TagListeGroupid);
                            }
                        }
                    }


                    //Orderby the list by TagListGroupId
                    lstRawMaterials = (from r in lstRawMaterials
                                       orderby r.TagListeGroupId
                                       select r).ToList();

                    this._RawMaterialList = lstRawMaterials;


                    var negRaw = (from n in ctx.NegativeRawMaterials
                                 from r in ctx.RawMaterials
                                 where n.RawMaterialId == r.Id && n.RecipeId == _Id
                                 select new
                                 {
                                     RecipeId = _Id,
                                     Flush = n.Flush,
                                     Id = r.Id,
                                     Name = r.Name,
                                     Number = r.MaterialNumber
                                 })
                                  .Select(x => new RawMaterials()
                                  {
                                      RecipeId = x.RecipeId,
                                      Id = x.Id,
                                      Name = x.Name,
                                      Number = x.Number,
                                      BatchFlush = Convert.ToInt16(x.Flush)
                                  }).ToList();

                    if (negRaw != null)
                    {
                        this._RawMaterialNegativeList = negRaw.ToList<RawMaterials>();
                    }
                   
                }

            }
        }

        /// <summary>
        /// Loads all recipes
        /// </summary>
        private void LoadRecipeList()
        {
            var recipe = (from r in ctx.Recipes
                          where r.Active == true
                          select r).ToList<APC.Common.Recipe>();

            if (recipe != null)
            {
                this._RecipeList = recipe;
            }
        }

        public void Save()
        {
            Int32 RecipeId = 0;

        try
        {
            if (ctx == null)
            {
                ctx = new DataHandlingDataContext(new Data().ConnectionString);
            }

            using (ctx)
            {

                APC.Common.Recipe r;

                switch (this._ActionType)
                {
                    case eActionType.TypeNew:
                        r = new APC.Common.Recipe();
                        r.RecipeName = _RecipeName;
                        r.RecipeNumber = _RecipeNumber;
                        r.RecBatchSize = _RecBatchSize; 
                        r.MinBatchSize = _MinBatchSize;
                        r.Description = _Note;
                        r.EndProductName = _EndProductName;
                        r.DefaultOrderQuantum = _DefaultOrderQuantum;
                        r.Active = true;
                        ctx.Recipes.InsertOnSubmit(r);
                        ctx.SubmitChanges(); //Save to database
                        RecipeId = r.Id; //Return new Id for saving related Ingredients (Raw Materials)
                        break;

                    case eActionType.TypeEdit:
                        r = ctx.Recipes.Single(u => u.Id == _Id);
                        r.Id = _Id;
                        r.RecipeName = _RecipeName;
                        r.RecipeNumber = _RecipeNumber;
                        r.RecBatchSize = _RecBatchSize;   
                        r.MinBatchSize = _MinBatchSize;
                        r.Description = _Note;
                        r.EndProductName = _EndProductName;
                        r.DefaultOrderQuantum = _DefaultOrderQuantum;
                        ctx.SubmitChanges(); //Save to database
                        RecipeId = _Id;
                        break;

                    case eActionType.TypeDeleteRecipe:
                        r = ctx.Recipes.Single(u => u.Id == _Id);
                        if (r != null)
                        {
                            r.Active = false;
                            ctx.SubmitChanges(); //Save to database
                        }
                       
                        break;

                    case eActionType.TypeDeleteRawMaterial:
                        RecipeIngredientsList rm;
                        rm = ctx.RecipeIngredientsLists.Single(u => u.RawMaterielId == _RawMaterialId && u.RecipeId == _Id);
                        ctx.RecipeIngredientsLists.DeleteOnSubmit(rm);
                        ctx.SubmitChanges(); //Save to database                    
                    break;
                }

                Log log = new Log();
                log.UserId = "sys";
                log.ObjectName = "Recipe";
                log.ObjectId = _Id.ToString();
                log.ObjectChangeType = _ActionType.ToString();
                log.LogMessage = "";
                log.Write();

                if (this._ActionType == eActionType.TypeEdit || this._ActionType == eActionType.TypeNew)
                {
                    //Loop all ingridients in recipe and detect whether rawmaterials has been added, modified or deleted
                    foreach (APC.RawMaterials raw in this._RawMaterialList)
                    {
                        RecipeIngredientsList ril;

                        
                        switch (raw.RecordStatus)
                        {
                            case RawMaterials.eRecordStatus.IsAdded:
                            ril = new RecipeIngredientsList();
                            ril.RecipeId = RecipeId;
                            ril.RawMaterielId = raw.Id;
                            ril.AmountProcent = raw.AmountProcent;
                            ril.ToleranceProcent = raw.Tolerance;
                            ctx.RecipeIngredientsLists.InsertOnSubmit(ril);

                            log.UserId = "sys";
                            log.ObjectName = "RecipeRawMaterials";
                            log.ObjectId = raw.Id.ToString();
                            log.ObjectChangeType = "RawMaterial added";
                            log.LogMessage = "Recipe.Id: " + Id.ToString() + " RawMaterialId: " + raw.Id.ToString();
                            log.Write();
                                break;

                            case RawMaterials.eRecordStatus.IsEdited:
                            ril = ctx.RecipeIngredientsLists.Single(u => u.RawMaterielId == raw.Id && u.RecipeId == raw.RecipeId);
                            ril.AmountProcent = raw.AmountProcent; 
                            ril.ToleranceProcent = raw.Tolerance;

                            log.UserId = "sys";
                            log.ObjectName = "RecipeRawMaterials";
                            log.ObjectId = raw.Id.ToString();
                            log.ObjectChangeType = "RawMaterial edited";
                            log.LogMessage = "Recipe.Id: " + Id.ToString() + " RawMaterialId: " + raw.Id.ToString();
                            log.Write();
                                break;

                            case RawMaterials.eRecordStatus.IsDeleted:
                            ril = ctx.RecipeIngredientsLists.Single(u => u.RawMaterielId == raw.Id && u.RecipeId == raw.RecipeId);
                            ctx.RecipeIngredientsLists.DeleteOnSubmit(ril);

                            log.UserId = "sys";
                            log.ObjectName = "RecipeRawMaterials";
                            log.ObjectId = raw.Id.ToString();
                            log.ObjectChangeType = "Removed from Recipe";
                            log.LogMessage = "Recipe.Id: " + Id.ToString() + " RawMaterialId: " + raw.Id.ToString();
                            log.Write();
                                break;

                        }
                    }

                    APC.Common.NegativeRawMaterial n;

                    //Insert negative rawmaterial items in table: NegativeRawMaterials
                    foreach (APC.RawMaterials nrm in this._RawMaterialNegativeList)
                    {
                        switch (nrm.RecordStatus)
                        {
                            case RawMaterials.eRecordStatus.IsAdded:
                                
                                n = new APC.Common.NegativeRawMaterial();
                                n.Flush = nrm.BatchFlush;
                                n.RecipeId = nrm.RecipeId;
                                n.RawMaterialId = nrm.Id;
                                ctx.NegativeRawMaterials.InsertOnSubmit(n);
                                break;

                            case RawMaterials.eRecordStatus.IsDeleted:
                                n = ctx.NegativeRawMaterials.FirstOrDefault(x => x.RecipeId == nrm.RecipeId && x.RawMaterialId == nrm.Id);
                                if (n != null)
                                {
                                    ctx.NegativeRawMaterials.DeleteOnSubmit(n);
                                  
                                }
                                break;

                            case RawMaterials.eRecordStatus.IsEdited:
                                n = ctx.NegativeRawMaterials.FirstOrDefault(x => x.RecipeId == nrm.RecipeId && x.RawMaterialId == nrm.Id);
                                if (n != null)
                                {
                                    n.Flush = nrm.BatchFlush; //Number of 'clean' batches to flush system to not contaminate 
                                }
                                break;


                            case RawMaterials.eRecordStatus.IsNotChanged:
                                //Do nothing!
                                break;
                        } 
                    }

                    ctx.SubmitChanges(); //Save to database        
                }
            }                 
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex); //Write to log!
        }
            
        }

        #endregion


        }

 

    }


